# -*- coding: utf-8 -*-
"""
Created on Wed May  8 15:34:20 2019

@author: nchji
"""
import datetime

from jili.core.printlog import print
from copy import deepcopy
import threading
import pickle,zmq,time,zlib
import os
from concurrent.futures import ThreadPoolExecutor
from functools import partial
from jili.tool.convert import str2time
print("Version:20211103")
import traceback
isnotry0=False
class SingletonType(type):
    _instance_lock = threading.Lock()
    def __call__(cls):
        if not hasattr(cls, "_instance"):
            with SingletonType._instance_lock:
                if not hasattr(cls, "_instance"):
                    cls._instance = super(SingletonType,cls).__call__()
        return cls._instance
class Counter(metaclass=SingletonType):
    def __init__(self,init=0):
        self.number=init
    def getid(self):
        self.number=self.number+1
        return self.number

counter=Counter()
sourceid=os.getpid()
def tobytes(msg):
    if not isinstance(msg,bytes):
        return bytes(msg,encoding='utf8')
    else:
        return msg
def tostring(msg):
    if isinstance(msg,str):
        return msg
    else:
        return str(msg,encoding='utf8')
def getsource(isreply=False):
    t={}
    t["pid"]=sourceid
    t["seqid"]=counter.getid()
    t["isreply"]=isreply
    return t
def calc_msgzip_batch_xingneng(msgtype,subject,s,batchs=[10,20,50,100,200]):
    for n in batchs:
        t0=datetime.datetime.now()
        s0,_ = encodemsg(msgtype,subject,s[:n],False)
        t1=datetime.datetime.now()
        s1,_ = encodemsg_zip(msgtype, subject, s[:n], False)
        t2 = datetime.datetime.now()
        m0 = decodemsg(s0)
        t3 = datetime.datetime.now()
        m1 = decodemsg_zip(s1)
        t4 = datetime.datetime.now()
        print("batch",n,"编码:长,时",len(s0),(t1-t0).total_seconds(),"解码:",(t3-t2).total_seconds(),
              "编码(zip):长,时",len(s1),(t2-t1).total_seconds(),"解码(zip):",(t4-t3).total_seconds(),"zip:",len(s1)/len(s0)
              )
def encodemsg(msgtype,subject="",msg={},isreply=False,s=""):
    if s=="":
        t=getsource(isreply)
    else:
        t=getsource()
    data = (t, msg)
    s0=tobytes(msgtype)+b"|"+tobytes(subject)+b"|"+pickle.dumps(data)
    return s0,t["seqid"]
        #return zlib.compress(s),t["seqid"]
    #self.__end_send.send(zlib.compress((s)

def decodemsg(s):
    #ms=zlib.decompress(s)
    # print(s)
    ms=s.split(b'|')
    m={}
    if len(ms)==3:
        m["0"]=tostring(ms[0])
        m["1"]=tostring(ms[1])
        data=pickle.loads(ms[2])
        m["s"]=data[0]
        m["2"]=data[1]
    elif len(ms)>3:
        m["0"]=tostring(ms[0])
        m["1"]=tostring(ms[1])
        ms0 = ms[2]
        for i in ms[3:]:
            ms0 = ms0+b"|"+i
        data = pickle.loads(ms0)
        m["s"] = data[0]
        m["2"] = data[1]
    else:
        print("error","decodemsg:len",len(ms),s)
    return m
def encodemsg_zip(msgtype,subject="",msg={},isreply=False,s=""):
    if s=="":
        t=getsource(isreply)
    else:
        t=getsource()
    data=(t,msg)
    s=pickle.dumps(data)
    s = zlib.compress(s)
    s0=tobytes(msgtype)+b"|"+tobytes(subject)+b"|"+s
    return s0,t["seqid"]
    #self.__end_send.send(zlib.compress((s)
def decodemsg_zip(s):
    #ms=zlib.decompress(s)
    # print(s)
    ms=s.split(b'|')
    m={}
    if len(ms)==3:
        m["0"]=tostring(ms[0])
        m["1"]=tostring(ms[1])
        data=pickle.loads(zlib.decompress(ms[2]))
        m["s"]=data[0]
        m["2"]=data[1]
    elif len(ms)>3:
        m["0"]=tostring(ms[0])
        m["1"]=tostring(ms[1])
        ms0 = ms[2]
        for i in ms[3:]:
            ms0 = ms0+b"|"+i
        data = pickle.loads(zlib.decompress(ms0))
        m["s"] = data[0]
        m["2"] = data[1]
    else:
        print("error","decodemsg:len",len(ms),s)
    return m
# mohe_boxs={}
class mohe():
    def __init__(self,recv_addrr=None,send_addrr=None,maxwokers=1,isautorunrecv=True,isnoblock=False,isnotry=False,auto_stop=None,blockrecv_outtime=5000,iscallmsg=False,iszip=False):
        """
        :param recv_addrr: 消息中间接收地址,None则不接收消息,支持多个地址,用逗号分隔,如:tcp://127.0.0.1:9114,tcp://127.0.0.1:9115
        :param send_addrr: 消息中间发送地址,None则不发送消息,支持多个地址,用逗号分隔,如:tcp://127.0.0.1:9114,tcp://127.0.0.1:9115
        :param maxwokers: 线程池最大线程数;消息会使用线程池处理到业务回调中;注意另起线程的影响
        :param isautorunrecv: 是否自动启动recv;自动模式下,recv_addrr必须有值,否则会报错,且启用单独线程处理消息的接受和处理;
        :param isnoblock: 是否阻塞模式:阻塞模式下,无消息时,接收线程会阻塞;否则定时跳出
        :param isnotry: 是否不捕获异常:默认捕获异常,,,捕获异常后,会打印异常信息,并返回None,如果不捕获异常,则会抛出异常,程序终止
        :param auto_stop: 自动停止时间
        :param blockrecv_outtime: 阻塞模式下的超时时间
        :param iscallmsg: 是否直接回调消息,默认不回调消息,而原始数据;

        key=recv_addrr+"."+send_addrr
        global mohe_boxs
        :param send_addrr:
        :param maxwokers:
        :param isautorunrecv:
        :param isnoblock:
        :param isnotry:
        :param auto_stop:
        :param blockrecv_outtime:
        """
        # key=recv_addrr+"."+send_addrr
        # global mohe_boxs
        # if key in mohe_boxs.keys():
        #     self=mohe_boxs[key]
        #     return None
        global isnotry0
        self.iscallmsg=iscallmsg
        if isnotry0 or isnotry:
            isnotry=True
            if iscallmsg:
                self.trycall = self.trycall_debug_msg
            else:
                self.trycall=self.trycall_debug
            self.__deal_block=self.__deal_block_debug
            self.__deal_noblock=self.__deal_noblock_debug
        else:
            if iscallmsg:
                self.trycall = self.trycall_nodebug_msg
            else:
                self.trycall = self.trycall_nodebug
            self.__deal_block = self.__deal_block_nodebug
            self.__deal_noblock = self.__deal_noblock_nodebug
        if iszip:
            self.encodemsg=encodemsg_zip
            self.decodemsg=decodemsg_zip
        else:
            self.encodemsg=encodemsg
            self.decodemsg=decodemsg
        self.isautorunrecv=isautorunrecv
        self.__context_recv = zmq.Context()
        self.__end_recv = self.__context_recv.socket(zmq.SUB)
        self.__context_send = zmq.Context()
        self.__end_send = self.__context_send.socket(zmq.PUB)
        self.recv_addrr=recv_addrr
        self.send_addrr=send_addrr
        if send_addrr is not None:
            if isinstance(send_addrr,list) or isinstance(send_addrr,tuple):
                for i in send_addrr:
                    print("mohe send_addrr:",i,"connect")
                    self.__end_send.connect(i)
            else:
                print("mohe send_addrr:", send_addrr, "connect")
                self.__end_send.connect(send_addrr)
        self.__onfunclist = {}
        self.__acknotify = {}
        self.__acknotify_sycn = {}
        self.msgseqkey = {}
        self.subscribe(self.ontinitmsgseqkey, msgtype="initevent", subject="")
        self.subscribe(self.onacknotify, msgtype="acknotify", subject=str(sourceid))
        self.initevent()
        self.executor = ThreadPoolExecutor(max_workers=maxwokers)
        self.isnoblock=isnoblock
        self.isstoprecv=False
        self.isrunning = True
        self.set_auto_stop(auto_stop)
        self.t1=None
        if recv_addrr is not None:
            self.__end_recv.setsockopt(zmq.TCP_KEEPALIVE, 1)
            self.__end_recv.setsockopt(zmq.TCP_KEEPALIVE_INTVL, 1)
            self.__end_recv.setsockopt(zmq.TCP_KEEPALIVE_IDLE, 60)
            self.__end_recv.setsockopt(zmq.TCP_KEEPALIVE_CNT, 5)
            if not self.isnoblock:
                if blockrecv_outtime:
                    self.__end_recv.setsockopt(zmq.RCVTIMEO, blockrecv_outtime)
            if isinstance(recv_addrr,list) or isinstance(recv_addrr,tuple):
                for i in recv_addrr:
                    print("mohe recv_addrr:", i, "connect")
                    self.__end_recv.connect(i)
            else:
                print("mohe recv_addrr:", recv_addrr, "connect")
                self.__end_recv.connect(recv_addrr)
            if isautorunrecv:
                self.t1 = threading.Thread(target=self.__deal, args=())
                # self.t1.setDaemon(True)
                self.t1.daemon = True  # 直接设置属性，不需要调用setDaemon方法
                self.t1.start()
                print("autorun",self.t1.name,"阻塞标识isnoblock:",self.isnoblock,"不try跳过isnotry",isnotry,"阻塞超时blockrecv_outtime",blockrecv_outtime,"是否直接回调消息",iscallmsg,"iszip",iszip)
            else:
                print("not autorun","阻塞标识isnoblock:",self.isnoblock,"不try跳过isnotry",isnotry,"阻塞超时blockrecv_outtime",blockrecv_outtime,"是否直接回调消息",iscallmsg,"iszip",iszip)
        time.sleep(0.1)
    def set_auto_stop(self,auto_stop):
        if auto_stop:
            self.auto_stop=[]
            for i in auto_stop:
                self.auto_stop.append(str2time(i))
        else:
            self.auto_stop=auto_stop
        print("mohe auto_stop:",self.auto_stop)
        # mohe_boxs[key]=self
    def close(self):
        self.stop()
        time.sleep(1)
        if self.t1:
            print(self.t1.name, "close stop")
        else:
            print("main close stop")
        self.__end_recv.close()
        self.__end_send.close()
        self.__context_recv.term()
        self.__context_send.term()

        # if self.t1:
        #     self.join()
    def stop(self):
        self.isrunning=False
        self.isstoprecv = True
    def start(self):
        if self.t1:
            self.t1.start()
    def join(self):
        if self.t1:
            self.t1.join()
    def run(self):
        print("runrecv isnoblock_main", "isnoblock", self.isnoblock, "")
        self.__deal()
    def isActive(self):
        return (self.__end_send.TCP_KEEPALIVE,self.__end_recv.TCP_KEEPALIVE)
    def setmax_workers(self,maxwokers):
        self.executor = ThreadPoolExecutor(max_workers=maxwokers)
    def subscribe(self,onfunc,msgtype="",subject="",issubject_end=True):
        rst=()
        if subject=="":
            s = tobytes(msgtype) + b"|"
        else:
            if issubject_end:
                s = tobytes(msgtype) + b"|" + tobytes(subject) + b"|"
            else:
                s = tobytes(msgtype) + b"|" + tobytes(subject)
        if msgtype not in self.__onfunclist.keys():
            self.__onfunclist[msgtype]={}
        if subject not in self.__onfunclist[msgtype].keys():
            self.__onfunclist[msgtype][subject]=[]
        if onfunc not in self.__onfunclist[msgtype][subject]:  # 去重多次注册函数，否则会多次
            self.__onfunclist[msgtype][subject].append(onfunc)
            self.__end_recv.subscribe(s)
        rst = (s, self.__onfunclist[msgtype][subject])

        #
        # if msgtype in self.__onfunclist.keys():
        #     if subject in self.__onfunclist[msgtype].keys():
        #         if onfunc not in self.__onfunclist[msgtype][subject]:#去重多次注册函数，否则会多次
        #             self.__onfunclist[msgtype][subject].append(onfunc)
        #             rst=(s,self.__onfunclist)
        #             self.__end_recv.subscribe(s)
        #             #self.__end_recv.setsockopt(zmq.SUBSCRIBE,s)
        #     else:
        #         self.__onfunclist[msgtype][subject]=[]
        #         self.__onfunclist[msgtype][subject].append(onfunc)
        #         rst=(s,self.__onfunclist)
        #         self.__end_recv.subscribe(s)
        # else:
        #     self.__onfunclist[msgtype]={}
        #     self.__onfunclist[msgtype][subject]=[]
        #     self.__onfunclist[msgtype][subject].append(onfunc)
        #     rst=(s,self.__onfunclist)
        #     self.__end_recv.subscribe(s)
        return rst
    def unsubscribe(self,msgtype="",subject="",onfunc=""):
        if subject!="" :
            s=tobytes(msgtype)+b"|"+tobytes(subject)+b"|"
            if msgtype in self.__onfunclist.keys():
                if subject in self.__onfunclist[msgtype].keys():
                    if onfunc=="":
                        del self.__onfunclist[msgtype][subject]
                        self.__end_recv.unsubscribe(s)
                    else:
                        self.__onfunclist[msgtype][subject].remove(onfunc)
                        if len(self.__onfunclist[msgtype][subject])==0:
                            del self.__onfunclist[msgtype][subject]
                            self.__end_recv.unsubscribe(s)
        else:
            s=tobytes(msgtype)+b"|"
            if msgtype in self.__onfunclist.keys():
                if onfunc=="":
                    del self.__onfunclist[msgtype]
                    self.__end_recv.unsubscribe(s)
                else:
                    if subject in self.__onfunclist[msgtype].keys():
                        self.__onfunclist[msgtype][subject].remove(onfunc)
                        if len(self.__onfunclist[msgtype][subject])==0:
                            del self.__onfunclist[msgtype][subject]
                            self.__end_recv.unsubscribe(s)
        return (s,self.__onfunclist)
    def isunduplicate(self,s):
        pid=s["pid"]
        seqid=s["seqid"]
        if pid in self.msgseqkey.keys():
            if seqid>self.msgseqkey[pid]:
                return True
            else:
                return False
        else:
            return True
    def ontinitmsgseqkey(self,msg):
        pid=msg["s"]["pid"]
        if pid in self.msgseqkey.keys():
            del self.msgseqkey[pid]

    def __impl_send(self,mstr):
        self.__end_send.send(mstr)
    def initevent(self):
        mstr,_=self.encodemsg("initevent","")
        self.__impl_send(mstr)
    def onacknotify(self,msg):
        seqid=msg["s"]["seqid"]
        msg["e"]=msg["2"]
        if seqid in self.__acknotify.keys():
            self.__acknotify[seqid](msg)
        elif seqid in self.__acknotify_sycn.keys():
            self.__acknotify_sycn[seqid].update(msg)
            self.__acknotify_sycn[seqid]["event"].set()
    def acksendreply(self,msg):
        t=msg["s"]
        t["isreply"]=False
        mstr,_=self.encodemsg("acknotify",str(msg["s"]["pid"]),msg={},isreply=False,s=t)
        self.__impl_send(mstr)

    def safesend(self,msgtype,subject,msg,acknotify):
        mstr,seqid=self.encodemsg(msgtype,subject,msg,isreply=True)
        self.__impl_send(mstr)
        self.__acknotify[seqid]=acknotify
        return seqid
    def safesend_sync(self,msgtype,subject,msg,timeout=45):
        mstr,seqid=self.encodemsg(msgtype,subject,msg,isreply=True)
        self.__impl_send(mstr)
        t=dict(event = threading.Event())
        self.__acknotify_sycn[seqid]=t
        t["event"].clear()
        timeout0=timeout/3
        rst={}
        rst["errcode"]=0
        if t["event"].wait(timeout0):
            if "e" in t.keys():
                rst["info"]=t["e"]
                del self.__acknotify_sycn[seqid]
            else:
                try:
                    self.__impl_send(mstr)
                except Exception as e:
                    print("error","safesend_sync",msgtype,subject,msg,e)
                t["event"].clear()
                if t["event"].wait(timeout0):
                    if "e" in t.keys():
                        rst["info"]=t["e"]
                        del self.__acknotify_sycn[seqid]
                    else:
                        try:
                            self.__impl_send(mstr)
                        except Exception as e:
                            print("error","safesend_sync",msgtype,subject,msg,e)
                        t["event"].clear()
                        if t["event"].wait(timeout0):
                            if "e" in t.keys():
                                rst["info"]=t["e"]
                                del self.__acknotify_sycn[seqid]
                            else:
                                rst["errcode"]=1
        if rst["errcode"]==1:
            print("warning","消息超时：",msgtype,subject,msg,timeout)
        return rst

    def send(self,msgtype,subject,msg):
        mstr,seqid=self.encodemsg(msgtype,subject,msg,isreply=False)
        #print("send",seqid)
        self.__impl_send(mstr)
        return seqid
    def getsubcall(self):
        return self.__onfunclist

    def __deal(self):
        if self.isnoblock:
            self.__deal_noblock()
        else:
            self.__deal_block()
    def trycall_debug_msg(self,call,args):
        # try:
        # print("trycall_nodebug_msg", args)
        call(args["2"])
        # except Exception as e:
        #     print("error","trycall:",call,e)
        #     print("error","data",args)
    def trycall_nodebug_msg(self,call,args):
        try:
            # print("trycall_nodebug_msg",args)
            call(args["2"])
        except Exception as e:
            print("error","trycall:",call,e)
            print("error","data",args)
            s = traceback.format_exc()
            print(s)
    def trycall_debug(self,call,args):
        # try:
        call(args)
        # except Exception as e:
        #     print("error","trycall:",call,e)
        #     print("error","data",args)
    def trycall_nodebug(self,call,args):
        try:
            call(args)
        except Exception as e:
            print("error","trycall:",call,e)
            print("error","data",args)
            s = traceback.format_exc()
            print(s)
    def __deal_block_debug(self):
        while self.isrunning:
            try:
                m = self.__end_recv.recv()
                print("debug", "block.m:", m)
                msg = self.decodemsg(m)
                print("debug", "decodemsg", msg)
                if msg:
                    msgtype = msg["0"]
                    subject = msg["1"]
                    if msgtype in self.__onfunclist.keys():
                        if "" in self.__onfunclist[msgtype].keys():
                            for call in self.__onfunclist[msgtype][""]:
                                call0 = partial(self.trycall, call)
                                if "s" in msg.keys():
                                    s = msg["s"]
                                    if s["isreply"]:
                                        self.acksendreply(msg)
                                        if self.isunduplicate(s):
                                            self.executor.submit(call0, (msg))
                                    else:
                                        self.executor.submit(call0, (msg))
                                else:
                                    self.executor.submit(call0, (msg))
                        if subject in self.__onfunclist[msgtype].keys():
                            for call in self.__onfunclist[msgtype][subject]:
                                call0 = partial(self.trycall, call)
                                if "s" in msg.keys():
                                    s = msg["s"]
                                    if s["isreply"]:
                                        self.acksendreply(msg)
                                        if self.isunduplicate(s):
                                            self.executor.submit(call0, (msg))
                                    else:
                                        self.executor.submit(call0, (msg))
                                else:
                                    self.executor.submit(call0, (msg))
                if self.isstoprecv:
                    break
            except zmq.error.Again:
                if self.auto_stop:
                    now=datetime.datetime.now()
                    t0=now.time()
                    if self.auto_stop[0]<=t0<=self.auto_stop[1]:
                        # self.stop()
                        self.close()
                        break
    def __deal_block_nodebug(self):
        while self.isrunning:
            try:
                m = self.__end_recv.recv()
                print("debug","block.m:",m)
                msg=self.decodemsg(m)
                print("debug","decodemsg",msg)
                if msg:
                    msgtype=msg["0"]
                    subject=msg["1"]
                    if msgtype in self.__onfunclist.keys():
                        if "" in self.__onfunclist[msgtype].keys():
                            for call in self.__onfunclist[msgtype][""]:
                                call0=partial(self.trycall,call)
                                if "s" in msg.keys():
                                    s=msg["s"]
                                    if s["isreply"]:
                                        self.acksendreply(msg)
                                        if self.isunduplicate(s):
                                            self.executor.submit(call0, (msg))
                                    else:
                                        self.executor.submit(call0, (msg))
                                else:
                                    self.executor.submit(call0, (msg))
                        if subject in self.__onfunclist[msgtype].keys():
                            for call in self.__onfunclist[msgtype][subject]:
                                call0 = partial(self.trycall, call)
                                if "s" in msg.keys():
                                    s=msg["s"]
                                    if s["isreply"]:
                                        self.acksendreply(msg)
                                        if self.isunduplicate(s):
                                            self.executor.submit(call0, (msg))
                                    else:
                                        self.executor.submit(call0, (msg))
                                else:
                                    self.executor.submit(call0, (msg))
                if self.isstoprecv:
                    break
            except zmq.error.Again:
                if self.auto_stop:
                    now=datetime.datetime.now()
                    t0=now.time()
                    if self.auto_stop[0]<=t0<=self.auto_stop[1]:
                        # self.stop()
                        self.close()
                        break
            except Exception as e:
                print("error","eventlib __deal:",e)
                s = traceback.format_exc()
                print(s)
                if self.isstoprecv:
                    break

    def __deal_noblock_debug(self):
        while self.isrunning:
            try:
                m = self.__end_recv.recv(flags=zmq.NOBLOCK)
                print("debug", "noblock.m:", m)
                if m:
                    msg=self.decodemsg(m)
                    #print("decodemsg",msg)
                    if msg:
                        msgtype=msg["0"]
                        subject=msg["1"]
                        if msgtype in self.__onfunclist.keys():
                            if "" in self.__onfunclist[msgtype].keys():
                                for call in self.__onfunclist[msgtype][""]:
                                    call0 = partial(self.trycall, call)
                                    if "s" in msg.keys():
                                        s=msg["s"]
                                        if s["isreply"]:
                                            self.acksendreply(msg)
                                            if self.isunduplicate(s):
                                                self.executor.submit(call0, (msg))
                                        else:
                                            self.executor.submit(call0, (msg))
                                    else:
                                        self.executor.submit(call0, (msg))
                            if subject in self.__onfunclist[msgtype].keys():
                                for call in self.__onfunclist[msgtype][subject]:
                                    call0 = partial(self.trycall, call)
                                    if "s" in msg.keys():
                                        s=msg["s"]
                                        if s["isreply"]:
                                            self.acksendreply(msg)
                                            if self.isunduplicate(s):
                                                self.executor.submit(call0, (msg))
                                        else:
                                            self.executor.submit(call0, (msg))
                                            #print("submit")
                                            #call(msg)
                                    else:
                                        self.executor.submit(call0, (msg))
                if self.isstoprecv:
                    break
            except zmq.ZMQError as e:
                if self.isstoprecv:
                    break
                if self.auto_stop:
                    now=datetime.datetime.now()
                    t0=now.time()
                    if self.auto_stop[0]<=t0<=self.auto_stop[1]:
                        # self.stop()
                        self.close()
                        break
                time.sleep(0.01)
    def __deal_noblock_nodebug(self):
        while self.isrunning:
            try:
                m = self.__end_recv.recv(flags=zmq.NOBLOCK)
                print("debug", "noblock.m:", m)
                if m:
                    msg=self.decodemsg(m)
                    #print("decodemsg",msg)
                    if msg:
                        msgtype=msg["0"]
                        subject=msg["1"]
                        if msgtype in self.__onfunclist.keys():
                            if "" in self.__onfunclist[msgtype].keys():
                                for call in self.__onfunclist[msgtype][""]:
                                    call0 = partial(self.trycall, call)
                                    if "s" in msg.keys():
                                        s=msg["s"]
                                        if s["isreply"]:
                                            self.acksendreply(msg)
                                            if self.isunduplicate(s):
                                                self.executor.submit(call0, (msg))
                                        else:
                                            self.executor.submit(call0, (msg))
                                    else:
                                        self.executor.submit(call0, (msg))
                            if subject in self.__onfunclist[msgtype].keys():
                                for call in self.__onfunclist[msgtype][subject]:
                                    call0 = partial(self.trycall, call)
                                    if "s" in msg.keys():
                                        s=msg["s"]
                                        if s["isreply"]:
                                            self.acksendreply(msg)
                                            if self.isunduplicate(s):
                                                self.executor.submit(call0, (msg))
                                        else:
                                            self.executor.submit(call0, (msg))
                                            #print("submit")
                                            #call(msg)
                                    else:
                                        self.executor.submit(call0, (msg))
                if self.isstoprecv:
                    break
            except zmq.ZMQError as e:
                if self.isstoprecv:
                    break
                time.sleep(0.01)
                if self.auto_stop:
                    now=datetime.datetime.now()
                    t0=now.time()
                    if self.auto_stop[0]<=t0<=self.auto_stop[1]:
                        # self.stop()
                        self.close()
                        break
            except Exception as e:
                print("error","eventlib __deal:",e)
                print("error","msg",m)
                s = traceback.format_exc()
                print(s)
                if self.isstoprecv:
                    break

class mohe_onlysend(mohe):
    def __init__(self,send_addrr):
        super().__init__(send_addrr=send_addrr)
class mohe_onlyautorecv(mohe):
    def __init__(self,recv_addrr,auto_stop=None):
        super().__init__(recv_addrr=recv_addrr,maxwokers=1,isautorunrecv=True,isnoblock=False,isnotry=False,auto_stop=auto_stop,blockrecv_outtime=5000)
class mohe_auto(mohe):
    def __init__(self,recv_addrr,send_addrr,auto_stop=None):
        super().__init__(send_addrr=send_addrr,recv_addrr=recv_addrr,maxwokers=1,isautorunrecv=True,isnoblock=False,isnotry=False,auto_stop=auto_stop,blockrecv_outtime=5000)
class mohe_advanced(mohe):
    def __init__(self,recv_addrr,send_addrr,auto_stop=None):
        super().__init__(send_addrr=send_addrr,recv_addrr=recv_addrr,maxwokers=1,isautorunrecv=False,isnoblock=False,isnotry=False,auto_stop=auto_stop,blockrecv_outtime=5000)

class mohe_msg(mohe):
    def __init__(self,recv_addrr,send_addrr,isautorunrecv=True,auto_stop=None,maxwokers=1,iszip=False):
        super().__init__(send_addrr=send_addrr, recv_addrr=recv_addrr, maxwokers=maxwokers, isautorunrecv=isautorunrecv,iscallmsg=True,
                         isnoblock=False, isnotry=False, auto_stop=auto_stop, blockrecv_outtime=5000,iszip=iszip)